﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.IO;

namespace Hallenberg.Core.IO
{
    /// <summary>
    /// The CSVFile class reads CSV files with and without headers.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CSV", Justification = "The name is intentional")]
    public class CSVFile
    {
        private char[] m_Separators;

        /// <summary>
        /// The rows of the CSV file.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "It is called upstream")]
        public CSVFileRowCollection Rows { get; private set; }

        /// <summary>
        /// The information read from the header.
        /// </summary>
        public ReadOnlyCollection<string> Header { get; private set; }

        /// <summary>
        /// Initializes collections and sets the default separators.
        /// </summary>
        public CSVFile()
        {
            m_Separators = new char[] { ',', ';' };
            this.Rows = new CSVFileRowCollection();
        }

        /// <summary>
        /// Get the separators used when parsing a CSV file.
        /// </summary>
        public char[] GetSeparators()
        {
            return m_Separators;
        }

        /// <summary>
        /// Set the separators used when parsing the CSV file.
        /// </summary>
        /// <param name="separators">The substitue separators</param>
        public void SetSeparators(char [] separators)
        {
            Debug.Assert(separators!=null);
            if(separators==null)
            {
                throw new ArgumentNullException("separators");
            }

            m_Separators=separators;
        }

        /// <summary>
        /// Load a CSV file without headers.
        /// </summary>
        /// <param name="filePath">Full path to a valid CSV file.</param>
        public void Load(string filePath)
        {
            Load(filePath, false);
        }

        /// <summary>
        /// Load a CSV file with or without headers.
        /// </summary>
        /// <param name="filePath">Full path to a valid CSV file.</param>
        /// <param name="hasHeader">Specifies whether or not the file has header information.</param>
        public void Load(string filePath, bool hasHeader)
        {
            Debug.Assert(!string.IsNullOrEmpty(filePath));
            if (string.IsNullOrEmpty(filePath))
            {
                throw new ArgumentNullException("filePath");
            }

            if (!File.Exists(filePath))
            {
                throw new FileNotFoundException();
            }

            using (TextReader tr = File.OpenText(filePath))
            {
                bool isFirstLine = true;
                string nextLine = tr.ReadLine();

                while (nextLine != null)
                {
                    if (isFirstLine && hasHeader)
                    {
                        CSVFileRow header = CSVFileRow.Parse(nextLine, m_Separators, null);
                        this.Header = new ReadOnlyCollection<string>(header.ToList());
                    }
                    else
                    {
                        CSVFileRow newRow = CSVFileRow.Parse(nextLine, m_Separators, this.Header);
                        if (newRow.Count() > 0)
                        {
                            this.Rows.Add(newRow);
                        }
                    }

                    isFirstLine = false;
                    nextLine = tr.ReadLine();
                }
            }

            if (this.Rows.Count == 0)
            {
                throw new InvalidOperationException("File is empty");
            }

            int firstRowColumnCount = this.Rows[0].Count;
            foreach (CSVFileRow row in this.Rows)
            {
                if (row.Count != firstRowColumnCount)
                {
                    throw new InvalidOperationException("Rows contains different number of columns");
                }
            }
        }
    }
}
